package com.zeyun.EasyOnlineShareCloud.common.web;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.zeyun.EasyOnlineShareCloud.common.convention.exception.AbstractException;
import com.zeyun.EasyOnlineShareCloud.common.enums.ResultErrorCodeEnums;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestNotUsableException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.Objects;
import java.util.Optional;

/**
 * 全局异常处理器
 */
@Component("globalExceptionHandlerByAdmin")
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

	/**
	 * 拦截参数验证异常
	 */
	@SneakyThrows
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public SaResult validMethodExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {
		BindingResult bindingResult = ex.getBindingResult();
		FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors());
		String exceptionStr = Optional.ofNullable(firstFieldError)
				.map(FieldError::getDefaultMessage)
				.orElse(StrUtil.EMPTY);
		log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr);
		return SaResult.error(ResultErrorCodeEnums.CLIENT_ERROR.message());
	}

	/**
	 * 拦截缺失参数异常
	 */
	@SneakyThrows
	@ExceptionHandler(value = MissingServletRequestParameterException.class)
	public SaResult handleMissingParamException(HttpServletRequest request,
												MissingServletRequestParameterException ex) {
		String paramName = ex.getParameterName(); // 获取缺失的参数名
		String message = String.format("请求参数缺失: %s", paramName);
		log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), message);
		return SaResult.error(ResultErrorCodeEnums.REQUEST_PARAM_MISSING.message());
	}

	/**
	 * 拦截未知接口异常
	 */
	@SneakyThrows
	@ExceptionHandler(value = NoResourceFoundException.class)
	public SaResult handleMissingParamException(HttpServletRequest request, NoResourceFoundException ex) {
		String path = ex.getResourcePath();
		String message = String.format("请求地址错误: %s", path);
		log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), message);
		return SaResult.error(ResultErrorCodeEnums.REQUEST_URL_ERROR.message());
	}

	/**
	 * 捕获 JSON 解析异常（类型不匹配、格式错误等）
	 */
	@SneakyThrows
	@ExceptionHandler(HttpMessageNotReadableException.class)
	public SaResult handleHttpMessageNotReadable(HttpServletRequest request,
												 HttpMessageNotReadableException ex) {
		String paramName = ex.getMessage(); // 获取缺失的参数名
		String message = String.format(ResultErrorCodeEnums.REQUEST_PARAM_FORMAT_ERROR.message() + ": %s", paramName);
		log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), message);
		return SaResult.error(ResultErrorCodeEnums.REQUEST_PARAM_FORMAT_ERROR.message());
	}

	/**
	 * 拦截SaToken的异常
	 */
	@ExceptionHandler(value = {NotLoginException.class})
	public SaResult notLoginException(HttpServletRequest request, NotLoginException ex) {
		return SaResult.error(ResultErrorCodeEnums.USER_NOT_LOGIN.message());
	}

	/**
	 * 拦截应用内抛出的异常
	 */
	@ExceptionHandler(value = {HttpMessageNotWritableException.class, AsyncRequestNotUsableException.class})
	public SaResult abstractException(HttpServletRequest request, Exception ex) {
		if (ex.getCause() != null) {
			log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString(),
					ex.getCause());
			return SaResult.error(ex.toString());
		}
		log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString());
		return SaResult.error(ex.toString());
	}

	/**
	 * 拦截应用内抛出的异常
	 */
	@ExceptionHandler(value = {AbstractException.class})
	public SaResult abstractException(HttpServletRequest request, AbstractException ex) {
		if (ex.getCause() != null) {
			log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString(),
					ex.getCause());
			return SaResult.error(ex.toString());
		}
		log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString());
		return SaResult.error(ex.toString());
	}


	/**
	 * 拦截未捕获异常
	 */
	@ExceptionHandler(value = Throwable.class)
	public SaResult defaultErrorHandler(HttpServletRequest request, Throwable throwable) {
		log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);
		// 注意，此处是为了聚合模式添加的代码，正常不需要该判断
		if (Objects.equals(throwable.getClass().getSuperclass().getSimpleName(),
				AbstractException.class.getSimpleName())) {
			String errorCode = ReflectUtil.getFieldValue(throwable, "errorCode").toString();
			String errorMessage = ReflectUtil.getFieldValue(throwable, "errorMessage").toString();
			log.error("[{}] {} [ex]{} {}", request.getMethod(), request.getRequestURL().toString(), errorCode,
					errorMessage);
			return SaResult.error(errorMessage);
		}
		return SaResult.error(ResultErrorCodeEnums.SYSTEM_RESOURCE_ERROR.message());
	}

	private String getUrl(HttpServletRequest request) {
		if (StrUtil.isEmpty(request.getQueryString())) {
			return request.getRequestURL().toString();
		}
		return request.getRequestURL().toString() + "?" + request.getQueryString();
	}
}
